package util;

import lombok.Getter;
import lombok.Setter;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 根據傳入的參照集合、變更集合，根據其中對象的字段解析出創建集合、更新的集合、刪除的集合
 */
@Setter
@Getter
public class ChangeSets<O, C> {

  private List<C> createItems;
  private List<C> updateItems;
  private List<O> deleteItems;

  public ChangeSets() {
    createItems = new LinkedList<>();
    updateItems = new LinkedList<>();
    deleteItems = new LinkedList<>();
  }

  /**
   * 解析出所有的創建、更新、刪除集合
   *
   * @param origin       參照集合
   * @param change       變更集合
   * @param originGetter 參照集合的解析字段
   * @param changeGetter 變更集合的解析字段
   * @param <O>          參照集合汎型
   * @param <C>          變更集合汎型
   * @param <R>          解析字段類型
   * @return ChangeSets<O, C>
   */
  public static <O, C, R> ChangeSets<O, C> analyze(List<O> origin, List<C> change, Function<O, R> originGetter, Function<C, R> changeGetter) {
    ChangeSets<O, C> changeSets = new ChangeSets<>();
    if (origin == null || origin.isEmpty()) {
      changeSets.createItems.addAll(change);
      return changeSets;
    }
    if (change == null || change.isEmpty()) {
      changeSets.deleteItems.addAll(origin);
      return changeSets;
    }
    Map<R, O> originMap = origin.stream().collect(Collectors.toMap(originGetter, obj -> obj));
    for (C obj : change) {
      R val = changeGetter.apply(obj);
      if (val == null || !originMap.containsKey(val))
        changeSets.createItems.add(obj);
      else {
        changeSets.updateItems.add(obj);
        originMap.remove(val);
      }
    }
    changeSets.deleteItems.addAll(originMap.values());
    return changeSets;
  }

  /**
   * 獲取創建、更新的合并集合
   *
   * @return List<C>
   */
  public List<C> getCreateAndUpdateItems() {
    List<C> list = new LinkedList<>();
    list.addAll(updateItems);
    list.addAll(createItems);
    return list;
  }

  /**
   * 判斷所有集合是否為空
   *
   * @return true-是,false-否
   */
  public boolean isEmpty() {
    return createItems.isEmpty() && updateItems.isEmpty() && deleteItems.isEmpty();
  }

}
